package com.ruoyi.aitcommon.utils;

import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.net.url.UrlPath;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.ruoyi.common.core.redis.RedisCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.ruoyi.aitcommon.vo.RuntimeVO;

@Component
public class RuntimeManager {
	@Autowired
	RedisCache redisCache;
	private String prefix="RunTime:";

//	flow运行时监控
//	主键=flowcode,记录该flowcode最后一次的运行状况
	private Map<String,RuntimeVO> runMap=new LinkedHashMap<String,RuntimeVO>();

//	记录运行日志
	private Map<String,List<String>> runLog=new LinkedHashMap<>();
	private Map<String,List<String>> runSQL=new LinkedHashMap<>();
	private Map<String,List<Map<String,Object>>> restCall=new LinkedHashMap<>();

	/**
	 * 在运行信息中,添加SQL运行信息
	 * @param sql
	 * @param db
	 */
	public void addSQLInfo(String sql,DBUtils db){
		String flowCode=FlowUtils.getFlowCode();
		if(ObjUtils.isBlank(flowCode))
			flowCode="default";
		String key=prefix+flowCode;
		key+=":SQL";

//		每次运行FLow,重置缓存
		if(!this.runSQL.containsKey(flowCode)) {
			this.runSQL.put(flowCode,new ArrayList<>());
			//重置redis
			redisCache.deleteObject(key);
		}
		final List<String> sqls = this.runSQL.get(flowCode);
//		日志只记录10条
		if(sqls.size()>=AITConst.RUNTIME_EXT_SIZE) return;

//		添加扩展信息
		String str="-- 数据库链接: "+db.toString()+"\r\n";
		str+=sql+"\r\n";
		sqls.add(str);
//		设置redis缓存列表
//		设置List对象,每次只需要设置新增部分
		redisCache.setCacheList(key,ObjUtils.toList(str));
	}
	public List<String> getSQLInfo(String flowCode){
		final List<String> sqls = this.runSQL.get(flowCode);
		if(ObjUtils.isBlank(sqls))
			return null;
		return sqls;
	}

	/**
	 * 在运行信息中,添加远程调用信息
	 * @param request
	 * @param body
	 * @param head
	 * @param response
	 */
	public void addRestCall(HttpRequest request, String body
			, Map<String,Object> head, HttpResponse response){
//		构造call map
		Map<String,Object> callmap=new LinkedHashMap<>();
		callmap.put("url",request.getUrl());
		callmap.put("head",head);
		callmap.put("body",body);
		callmap.put("method",request.getMethod().toString());
		if(response!=null)
			callmap.put("response",response.body());

		String flowCode=FlowUtils.getFlowCode();
		if(ObjUtils.isBlank(flowCode))
			flowCode="default";
		String key=prefix+flowCode;
		key+=":RestCall";

//		每次运行Flow,重置缓存
		if(!this.restCall.containsKey(flowCode)) {
			this.restCall.put(flowCode,new ArrayList<>());
			//重置redis
			redisCache.deleteObject(key);
		}
		final List<Map<String, Object>> calls = this.restCall.get(flowCode);
//		日志只记录10条
		if(calls.size()>AITConst.RUNTIME_EXT_SIZE) return;

//		添加扩展信息
		calls.add(callmap);
//		设置redis缓存列表
//		设置List对象,每次只需要设置新增部分
		redisCache.setCacheList(key,ObjUtils.toList(callmap));
	}

	public List<Map<String, Object>> getRestCall(String flowCode){
		final List<Map<String, Object>> calls = this.restCall.get(flowCode);
		if(ObjUtils.isBlank(calls))
			return null;
		return calls;
	}

	/**
	 * 判断当前是否有流程在运行
	 */
	public boolean isFLowRun(){
		if(runMap==null||runMap.size()==0)
			return false;
		for(String key:runMap.keySet()){
			final RuntimeVO vo = runMap.get(key);
			if(vo.getStatus()==1)
				return true;
		}
		return false;
	}

	/**
	 * 创建FLOW监控
	 * 如果之前存在旧信息,先删除
	 */
	public void openAITInfo() {
		String flowCode=FlowUtils.getFlowCode();
//		每次运行FLow,清除旧信息
		if(runMap.containsKey(flowCode))
			runMap.remove(flowCode);
		if(runLog.containsKey(flowCode))
			runLog.remove(flowCode);
		if(runSQL.containsKey(flowCode))
			runSQL.remove(flowCode);
		if(restCall.containsKey(flowCode))
			restCall.remove(flowCode);

//		初始新信息
		RuntimeVO vo=new RuntimeVO();
		vo.setFlowID(FlowUtils.getFlowID());
		vo.setFlowCode(flowCode);
		vo.setFlowName(FlowUtils.getFlowName());
		vo.setStatus(1);//1-运行
		vo.setStartTime(FlowUtils.getStartDate());
		runMap.put(flowCode, vo);
	}
	
	/**
	 * 关闭FLOW监控
	 * 不删除Runtime信息
	 */
	public void closeAITInfo() {
		String flowCode=FlowUtils.getFlowCode();
		if(runMap.containsKey(flowCode)) {
			RuntimeVO vo = runMap.get(flowCode);
			Date endTime=new Date();
			vo.setEndTime(endTime);
			//运行时间,毫秒
			long runTime=endTime.getTime()-vo.getStartTime().getTime();
			vo.setRunTime(runTime);

//			关闭运行监控时,做警告检查
			if(runTime>AITConst.WARN_RUNTIME)
				this.appendLog(RuntimeVO.LOGWARN,"运行时间超过预警时长[{}]毫秒",AITConst.WARN_RUNTIME);
			if(FlowUtils.getErrorCnt()>0)
				this.appendLog(RuntimeVO.LOGWARN,"存在错误数据[{}]条",FlowUtils.getErrorCnt());
			if(FlowUtils.getSuccessCnt()>AITConst.WARN_UPDATE_NUM)
				this.appendLog(RuntimeVO.LOGWARN,"成功处理数据超过{}条,请检查缓存过滤是否正确",AITConst.WARN_UPDATE_NUM);

//			设置关闭状态,并刷新状态名称
			vo.setStatus(0);//0-关闭
		}
	}

	public void appendLog(String log,Throwable ex) {
		StringBuilder err=new StringBuilder();
		err.append(log);
		err.append(" 错误消息: "+ExceptionUtils.getDetailInfo(ex));
		appendLog(RuntimeVO.LOGERR,err.toString());
	}

	/**
	 * RunTime中记录日志
	 * @param type 日志类型,数字越高,错误级别越严重
	 * @param log 日志信息
	 * @param args 日志参数,格式 {}
	 */
	public void appendLog(int type,String log,Object... args) {
		if(FlowUtils.getFlowID()<=0) return;

//			存在上下文
//			获取日志对象
		if(!runLog.containsKey(FlowUtils.getFlowCode())){
			runLog.put(FlowUtils.getFlowCode(),new ArrayList<>());
		}
		final List<String> logs = runLog.get(FlowUtils.getFlowCode());

//			添加日志
		String msg = StrUtil.format(log, args);
		if(type==RuntimeVO.LOGINFO)
			msg=" INFO "+msg;
		else if(type==RuntimeVO.LOGWARN){
			setVOErr(type,msg);
			msg=" WARN "+msg;
		}else if(type==RuntimeVO.LOGERR){
			setVOErr(type,msg);
			msg=" ERROR "+msg;
		}
		else
			msg=" NOTYPE "+msg;

//			记录RunTime日志
		msg=DateTime.now()+msg;
//			限定Log长度
		if(msg.length()>AITConst.LOG_STRING_SIZE) msg=StrUtil.sub(msg,0,AITConst.LOG_STRING_SIZE);
		if(logs.size()>AITConst.LOG_SIZE) return;
//			过滤重复信息
		String last="";
		if(logs.size()>0) last=logs.get(logs.size()-1);
		if(msg.equals(last)) return;

		logs.add(msg);
	}
	/**
	 * 设置当前运行方法
	 * @param method
	 */
	public void setMehtod(String method) {
		String flowCode=FlowUtils.getFlowCode();
		if(runMap.containsKey(flowCode)) {
			RuntimeVO vo = runMap.get(flowCode);
			vo.setMethod(method);
		}
	}

	/**
	 * 设置RuntimeVO的最后一笔 警告或是错误
	 * @param logStatus
	 * @param err
	 */
	public void setVOErr(int logStatus,String err){
		String flowCode=FlowUtils.getFlowCode();
		if(runMap.containsKey(flowCode)) {
			RuntimeVO vo = runMap.get(flowCode);
			if(logStatus>=vo.getLogStatus()){
//				错误级别越高越严重,记录级别最高的最后一笔消息
				vo.setLogStatus(logStatus);
				vo.setErrMsg(err);
			}
		}
	}
	public String getRunLog(String flowcode){
		if(ObjUtils.isBlank(flowcode)) return null;

		if(this.runLog.containsKey(flowcode)){
			final List<String> logs = this.runLog.get(flowcode);
			StringBuilder retlog=new StringBuilder();
			for (String log : logs) {
				retlog.append(log+"\r\n");
			}
			return retlog.toString();
		}
		return null;
	}

	public RuntimeVO getRunInfo(String flowcode){
		if(ObjUtils.isBlank(flowcode)) return null;

		if(this.runMap.containsKey(flowcode)){
			RuntimeVO vo = runMap.get(flowcode);
			if(vo.getStatus()==1) {
//				如果 运行中 ,动态计算运行时间
				Date endTime=new Date();
				long runTime=endTime.getTime()-vo.getStartTime().getTime();
				vo.setRunTime(runTime);
			}
			return vo;
		}
		return null;
	}
	/**
	 * 获取监控清单
	 * @param status -1=所有 0=关闭 1=运行中
	 * @return
	 */
	public List<RuntimeVO> getRunList(int status){
		List<RuntimeVO> retlist=new ArrayList<RuntimeVO>();
		
//		构造清单
		for(String key:this.runMap.keySet()) {
			RuntimeVO vo = this.getRunInfo(key);
			if(vo.getStatus()==status||status==-1) {
				retlist.add(vo);
			}
		}
		
//		对清单进行排序
//		返回值为正数的在前面
		retlist.sort((item1,item2)->{
			int ret=0;
			if(item1.getStatus()!=item2.getStatus()) {
//				运行中的优先 status 0=关闭 1=运行中
				ret=item2.getStatus()-item1.getStatus();
			} else if(item1.getLogStatus()!=item2.getLogStatus()){
//				日志级别高的排在前面,LOGINFO=0;LOGWARN=1;LOGERR=2;
				ret=item2.getLogStatus()-item1.getLogStatus();
			} else {
//				如果状态相同,开始时间靠前的优先
				Long ret1=item2.getStartTime().getTime() - item1.getStartTime().getTime();
				ret=ret1.intValue();
			}
				
			return ret;
		});
		
		return retlist;
		
	}
}
